package com.maas.util.mybatis.jpa.littlenb.statement;

import com.littlenb.mybatisjpa.statement.AbstractStatementFactory;
import com.littlenb.mybatisjpa.statement.StatementFactory;
import com.littlenb.mybatisjpa.support.Constant;
import com.littlenb.mybatisjpa.type.SelectorStrategy;
import com.maas.util.mybatis.jpa.littlenb.annotation.mapper.SelectDefinition;
import com.maas.util.mybatis.jpa.littlenb.common.JbaLoggerUtil;
import com.maas.util.mybatis.jpa.littlenb.common.JpaTools;
import com.maas.util.mybatis.jpa.littlenb.constant.ResultMapConstants;
import com.maas.util.mybatis.jpa.littlenb.meta.PersistentMeta;
import com.maas.util.mybatis.jpa.littlenb.support.core.MyMybatisStatementAdapter;
import com.maas.util.mybatis.jpa.littlenb.support.template.select.SelectCertainSqlTemplate;
import com.maas.util.mybatis.jpa.littlenb.support.template.select.SelectIgnoreNullSqlTemplate;
import com.maas.util.mybatis.jpa.littlenb.support.template.select.SelectSqlTemplate;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.session.Configuration;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
 * @select 扫描器
 * 修改自 mybatis-jba
 * @author sway.li ->zxm
 */
public class SelectStatementFactory extends AbstractStatementFactory implements MyStatementBuildable {

  public static final StatementFactory INSTANCE = new SelectStatementFactory();
  MyMybatisStatementAdapter adapter;


  /**
   * AnnotationStatementScanner 判断 为 SelectDefinition 会调用MyStatementBuildable.parseStatement
   * @param adapter
   *            Adapter for {@see MappedStatement}
   * @param persistentMeta
   *            Entity持久化元数据
   * @param method
   */
  @Override
  public void parseStatement(MyMybatisStatementAdapter adapter, PersistentMeta persistentMeta, Method method) {
    this.adapter = adapter;
    SelectDefinition definition = method.getAnnotation(SelectDefinition.class);
    Class adapterResultType =persistentMeta.getType();
    String adapterResultMapId = ResultMapConstants.DEFAULT_NAMESPACE + "." + persistentMeta.getEntityName();

    Class<?> type = super.recognizeEntityType(method, adapter.getMapper());
    String methodName = method.getName();
    //拦截某个实体类
    if("RcEvaluationDto".equals(type.getSimpleName())){
    }
    if("getStaPage".equals(methodName)){
      System.out.println("SelectStatementFactory::"+type);
    }

    if (definition != null) {
      Class resultType =definition.resultType();
      String resultMapId =definition.resultMapId();
      if(resultType != SelectDefinition.class){
        adapterResultType =resultType;
      }

      //0
      if(JpaTools.isFalse(resultMapId)){
        adapterResultMapId =null;
      }
      if(!JpaTools.isEmpty(resultMapId)){
        adapterResultMapId =adapterResultMapId;
      }

    }

    // 方法名
    adapter.setMethodName(method.getName());
    // 参数类型
    if (method.getParameterTypes().length > 0) {
      // Mybatis mapper 方法最多支持一个参数,先设置成Object.class,mybatis会在sql中解析
      adapter.setParameterTypeClass(Object.class);
    } else {
      adapter.setParameterTypeClass(void.class);
    }



    // sqlScript
    adapter.setSqlScript(buildSQL(persistentMeta, method));

    // 返回值类型
    //adapter.setResultType(persistentMeta.getType())
    adapter.setResultType(adapterResultType);

    //xiresultmap
    //adapter.setResultMapId(ResultMapConstants.DEFAULT_NAMESPACE + "." + persistentMeta.getEntityName())
    adapter.setResultMapId(adapterResultMapId);

    adapter.setSqlCommandType(SqlCommandType.SELECT);

    // 主键策略
    adapter.setKeyGenerator(new NoKeyGenerator());
    adapter.setKeyProperty(null);
    adapter.setKeyColumn(null);

    //修改sql驱动
    adapter.setLanguageDriver(Constant.XML_LANGUAGE_DRIVER);
    adapter.parseStatement();
  }


  /**
   * AnnotationStatementScanner 判断 SelectDefinition 后不会调用 这个 StatementFactory.parseStatement
   * 返回MappedStatement 用于mybatis 的configuration.addMappedStatement(statement);
   * @param configuration
   * @param method
   * @param targetClass
   * @return
   */
  @Override
  public MappedStatement parseStatement(Configuration configuration, Method method,
                                        Class<?> targetClass) {
    String resourceName = targetClass.toString();


    if (!configuration.isResourceLoaded(resourceName)) {
      configuration.addLoadedResource(resourceName);
    }
    String targetClassName = targetClass.getName();
    Class<?> type = super.recognizeEntityType(method, targetClass);
    String methodName = method.getName();
    //拦截某个实体类

    LanguageDriver languageDriver = Constant.XML_LANGUAGE_DRIVER;
    SqlSource sqlSource = languageDriver
        .createSqlSource(configuration, "<script> " + parseSQL(method, type) + "</script>",
            Object.class);


    String statementId = targetClassName + "." + method.getName();

    //MappedStatement 建构器
    MappedStatement.Builder builder = new MappedStatement.Builder(configuration, statementId,
        sqlSource, SqlCommandType.SELECT);


    //builder.resultSetType(getResultSetType())


    //预编译：statementType标记使用什么的对象操作SQL语句
    /**
     * 取值说明：
     * 1、STATEMENT:直接操作sql，不进行预编译，获取数据：$—Statement
     * 2、PREPARED:预处理，参数，进行预编译，获取数据：#—–PreparedStatement:默认
     * 3、CALLABLE:执行存储过程————CallableStatement
     */
    builder.resource(super.recognizeResource(targetClassName)).lang(languageDriver)
        .statementType(StatementType.PREPARED);



    MappedStatement mappedStatement = builder.build();
    return mappedStatement;
  }

  public static String getParmName(Method method) {
    Class<?>[] classArr = method.getParameterTypes();
    Annotation[][] parameterAnnotations = method.getParameterAnnotations();
    String parmName ="";
    for (Annotation[] annotations : parameterAnnotations) {
      for (Annotation annotation : annotations) {
        //获取注解名
        String name = annotation.annotationType().getSimpleName();
        if(annotation.annotationType() == Param.class){
          String  parmNameTemp= ((Param) annotation).value();
          if(!"page".equals(parmNameTemp)){
            parmName = parmNameTemp;
            break;
          }

        }
      }
    }
    return parmName;
  }

  /**
   * 生成sql
   * @param method
   * @param type
   * @return
   */
  private String parseSQL(Method method, Class<?> type) {
    SelectDefinition definition = method.getAnnotation(SelectDefinition.class);
    if (definition == null) {
      throw new RuntimeException(
              "'" + method.getName() + "' method is not annotation with 'SelectDefinition'.");
    }

    String methodName = method.getName();
    //拦截某个实体类
    if("RcEvaluation".equals(type.getSimpleName())){

    }
    if("getStaPage".equals(methodName)){
      System.out.println("SelectStatementFactory::"+type);
    }
    String certainNameDef =definition.certainName();
    String[] neFeildArr = definition.neFeildArr();
    String[] likeFeildArr= definition.likeFeildArr();
    String certainName =getParmName(method);
    String groupBy =definition.groupBy();
    String selectParm =definition.selectParm();

    if(!JpaTools.isEmpty(certainNameDef)){
      certainName =certainNameDef;
    }

    SelectorStrategy strategy = definition.strategy();

    if(type.equals(String.class)){
      type = definition.domainClass();
      JbaLoggerUtil.log.error( method.getName()+ "SelectDefinition 暂时不支持 字符串查询"+ type);
    }


    String sql = "";

    //通过模板和注解构建sql语句
    SelectSqlTemplate selectSqlTemplate = new SelectSqlTemplate();
    selectSqlTemplate.selectParm=selectParm;
    sql =selectSqlTemplate.parseSQL(type);


    //TODO 动态生成 where 语句
    if (SelectorStrategy.IGNORE_NULL.equals(strategy)) {
      /**
       * @SelectDefinition( where   此时where 无效 。。。
       */
      sql += "" + SelectIgnoreNullSqlTemplate.INSTANCE.parseSQL(type);
    }else if (SelectorStrategy.CERTAIN.equals(strategy)) {
      /**
       * 如userId 查询
       * @SelectDefinition( where ="userId = #{"+ Constant.CERTAINTY_ENTITY_KEY+".userId}" 。。。
       */
      /**
       *  不限制查询字段
       * @SelectDefinition( where ="" 。。。
       */
      SelectCertainSqlTemplate template =new SelectCertainSqlTemplate();
      //sql +=  SelectCertainSqlTemplate.INSTANCE.parseSQL(type);
      template.neFeildArr =neFeildArr;
      template.likeFeildArr =likeFeildArr;
      template.where =definition.where();
      template.emptyEqFeildArr=definition.emptyEqFeildArr();
      template.dateParmArr =definition.dateParm();

      template.dateSearchArr=definition.dateSearch();

      sql += template.parseSQLEx(type , certainName);

    }else{

      /**
       *  限制查询字段
       * @SelectDefinition( where ="userId = #{userId}" 。。。
       *
       */
      //where
      if (!"".equals(definition.where())) {
        sql = sql + " where " + definition.where();
      }
    }

    //TODO 动态生成 ORDER_BY 语句
    if (!"".equals(definition.order())) {
      sql = sql + " order by " + definition.order();
    }

    if (JpaTools.isNotEmpty(groupBy)) {
      sql = sql + " group by " + definition.groupBy();
    }
    return sql;
  }

  private SelectorStrategy recognizeStrategy(Method method) {
    SelectDefinition definition = method.getAnnotation(SelectDefinition.class);
    if (definition == null) {
      throw new RuntimeException(
          "'" + method.getName() + "' method is not annotation with 'SelectDefinition'.");
    }
    return definition.strategy();
  }

  @Override
  public String buildSQL(PersistentMeta persistentMeta, Method method) {
    //TODO 现在必须传参数，改为可以不传参数
    Class<?> type = super.recognizeEntityType(method, adapter.getMapper());
    //return  parseSQL(method, type);
    return "<script> " + parseSQL(method, type) + "</script>";

  }

}
